<?php
/**
 * 五维健康测评算法
 * Created by PhpStorm.
 * User: yuanjy
 * Date: 2017/9/2
 * Time: 16:41
 */

namespace App\Services\Algo;

use App\Interf\Calculable;
use TestingItemService;
use App\Models\LogHelper;

class FiveDimensionTestingAlgo extends BaseTestingAlgo implements Calculable
{
    public $allAnswers ;
    private $typeFourExpress ;

    public function __construct($tid, $answerStr, $typeFourExpress = null, $isConvertAll = true)
    {
        parent::__construct($tid, $answerStr);
        $this->typeFourExpress = $typeFourExpress;
        if ($isConvertAll) {
            $this->convertAllAnswers();
        }
    }

    public function calculate()
    {
        return $this->entrance();
    }

    /**
     * 将拼装后的答案转换成字符串
     * @return string
     */
    public function resultToStr(){
        return $this->arrToStr($this->answers);
    }

    public function getAllAnswer(){
        return $this->answers;
    }

    /**
     * 根据表达式计算一个值
     * @param array $items
     * @return int
     */
    public function calculateByExpression($items)
    {
        $score = 0;
        foreach ($items as $item) {
            if ($item['question_type'] == 6) {
                $score = $this->getSingleItemWeight($item['expression']);
            } else {
                $score = $this->fourcalculate($this->answers, $item['expression']);
            }
            $obj = [
                'index' => $item['index'],
                'weight' => $score,
                'answer' => '',
            ];
            array_push($this->answers, $obj);
        }
        return $score;
    }

    /**
     * 计算测评所得分数
     * @return float|null
     */
    protected function entrance() {
        $config = [
            '40' => [1, 5, 15, 40, 1000],
            '41' => [7, 15, 35, 65, 1000],
            '42' => [0, 5, 20, 40, 1000],
            '46' => [1, 6, 20, 39, 1000],
            '47' => [2, 10, 40, 80, 1000],
        ];
        $totalScore = 0;
        foreach ($config as $key => $arr) {
            if ($key == 46) {
                $answer = $this->getAnswerObjByIndex(44);
                if ($answer['weight'] == 1) {
                    $totalScore += 12;
                    continue;
                }
            }
            $answer = $this->getAnswerObjByIndex($key);
            foreach ($arr as $key1 => $value) {
                if ($answer['weight'] <= $value) {
                    $totalScore += (5 - $key1) * 4;
                    break;
                }
            }
        }

        return $totalScore;
    }

    protected function convertAllAnswers(){
        LogHelper::log('before calculate BMI=============>');
        LogHelper::log($this->answers);
        $BMIData = [];

        //先计算43题BMI
        LogHelper::log('this->typeFourExpress=============>');
        LogHelper::log($this->typeFourExpress);
        if($this->typeFourExpress){
            foreach ($this->typeFourExpress as $d){
                if($d['index'] == 43){
                    array_push($BMIData, $d);
                    break;
                }
            }

            $result = $this->fourTypeArray($BMIData, $this->answers);
            foreach($result as $v){
                array_push($this->answers, $v);
            }
        }
        LogHelper::log('after calculate BMI=============>');
        LogHelper::log($this->answers);
        $types = 6;
        $dymTypeData = TestingItemService::getItemsByQuestionType($this->tid, $types, '_SIX');
        if(count($dymTypeData) > 0){
            foreach ($dymTypeData as $data){
                $expressStr = $data['expression'];
                $weight = $this->getSingleItemWeight($expressStr);
                $obj['index'] = $data['index'];
                $obj['answer'] = '';
                $obj['weight'] = $weight;
                array_push($this->answers, $obj);
            }
        }
        LogHelper::log('after CALCULATE TYPE SIX=============>');
        LogHelper::log($this->answers);

        //先合并所有的answer
        if($this->typeFourExpress){
            $fourTypeResult = $this->fourTypeArray($this->typeFourExpress, $this->answers);
            foreach($fourTypeResult as $v){
                if ($v['index'] == 43) {
                    continue;
                }
                array_push($this->answers, $v);
            }
        }

        LogHelper::log('after CALCULATE TYPE FOUR=============>');
        LogHelper::log($this->answers);
    }

    /**
     * 0<=$4&&$4<80:$4##0
     * 80<=$4&&$4<85:$4##2
     * 85<=$4&&$4<300:$4##3
     * @param $expressionStr
     * @return array
     */
    protected function convertExpression($expressionStr){
        $arr = [];
        $expressions = explode("|||",$expressionStr);
        if(count($expressions)>0){
            foreach ($expressions as $e){
                $data = explode('##',$e);
                $relateExp = $data[0];
                // 解析这种形式的表达式0<=$4&&$4<80:$4
                $condArr = explode(':',$relateExp);
                //如果不存在则构造一个
                $condition = $this->compactConditionStr($condArr[0]);
                $obj['condition'] = $condition;
                //拆分条件表达式中的所以和操作符号($和@)
                if($operateObj = $this->compactOperateStr($condArr[1])){
                    foreach ($operateObj as $key => $val){
                        $obj[$key] = $val;
                    }
                }else{
                    $obj['index'] = -1;
                    $obj['operate'] = '';
                }

                $obj['weight'] = $data[1];
                array_push($arr,$obj);
            }
        }
        return $arr;
    }


    /**
     * 获取单个type为6的类型的权重
     * @param $expressStr
     * @return int
     */
    protected function getSingleItemWeight($expressStr){
        $expressionList = $this->convertExpression($expressStr);
        $weight = 0;
        foreach ($expressionList as $exp){
            $answerObj = $this->getAnswerObjByIndex($exp['index'],$this->answers);
            $replaceStr = '';
            switch ($exp['operate']){
                case '@':
                    $replaceStr = $answerObj['weight'];
                    break;
                case '$':
                    $replaceStr = $answerObj['answer'];
                    break;
            }
            $search = $exp['operate'].$exp['index'];
            //首先替换表达式  $val=0<@23&&@23<4?1:0 中的@23，
            //同时下面的$val变量一定要与表达式中的$val保持一致，否则$val条件判断值取不到结果。
            $val = str_replace($search,$replaceStr,$exp['condition']);
            //通过php的eval函数计算php的表达式
            eval($val);
            if($val){
                //如果条件表达式条件成立则去权重。
                $weight = $exp['weight'];
                break;
            }
        }
        return $weight;
    }

    /**
     * 组装条件判断表达是表达式 形如《10<@23<=20这种
     * @param string $condition
     * @return string
     */
    protected function compactConditionStr($condition){
        //如果不存在则构建一个假的条件表达式
        $condition = $condition ? $condition : '1==0';
        //注意此处是php中的表达式，一定要带上分号，否则会出错。
        $condition = '$val='.$condition.'?1:0;';
        return $condition;
    }

    /**
     * 拆分操作表达式 形如@23或者$23这种
     * @param string $operateStr
     * @return array
     */
    protected function compactOperateStr($operateStr){
        $obj = [];
        $operateArr = ['@','$'];
        $firstLetter = substr($operateStr,0,1);
        if(in_array($firstLetter,$operateArr)){
            $obj['operate'] = $firstLetter ;
            $obj['index'] = (int)substr($operateStr,1);
        }
        return $obj;
    }
}